/**
AirwayPathfinder for X-Plane Flight Simulator. Calculates the shortest path between two NavAids. 
Copyright (C) 2011  Nils Buletta

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 **/

package de.buletta.airwaypathfinder;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

import java.io.FileNotFoundException;
//import java.io.FileOutputStream;
//import java.io.FileWriter;
//import java.io.IOException;
import java.io.PrintWriter;

import java.text.DecimalFormat;
import java.util.ArrayList;
//import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Locale;
//import java.util.Set;
//import java.util.prefs.BackingStoreException;
//import java.util.prefs.Preferences;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import org.jdesktop.swingx.JXMapKit;
import org.jdesktop.swingx.JXMapKit.DefaultProviders;
import org.jdesktop.swingx.JXMapViewer;
import org.jdesktop.swingx.mapviewer.AbstractTileFactory;

import org.jdesktop.swingx.mapviewer.Waypoint;

/**
 * The GUI for the Pathfinder program.
 * 
 * @author Nils Buletta
 * @version 0.1
 */
public class Gui extends JFrame implements ActionListener {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	protected JXMapKit mapKit = new JXMapKit();
	protected DataHolder holder;

	protected NavsRenderer renderer = new NavsRenderer();
	protected PathWaypointPainter<JXMapViewer> painter;
	protected EarthNav nav1;
	protected EarthNav nav2;
	protected boolean toggle = false;
	protected JTextField nav1Text;
	protected JTextField nav2Text;
	protected JPanel southPanel;
	protected JPanel southPanel2;
	protected ButtonGroup layerGroup;

	protected JTextField earthNavTextField;
	protected JTextField fixTextField;
	protected JTextField awyTextField;
	protected JTextField airportTextField;
	protected JTextField fmsStoreTextField;
	protected JPanel prefPanel;

	protected JButton earthNavPathButton;
	protected JButton fixPathButton;
	protected JButton awyPathButton;
	protected JButton airportPathButton;
	protected JButton fmsStorePathButton;
	protected JFileChooser fileChooser;

	private void createAndShowGui() {

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//this.setSize(800, 600);

		MenuBar menuBar = new MenuBar();
		Menu fileMenu = new Menu("File");

		MenuItem preferencesMenuItem = new MenuItem("Preferences");
		preferencesMenuItem.setActionCommand("preferences");
		preferencesMenuItem.addActionListener(this);
		fileMenu.add(preferencesMenuItem);

		menuBar.add(fileMenu);

		setMenuBar(menuBar);

		// increase number of threads used to load tiles for the map
		((AbstractTileFactory) mapKit.getMainMap().getTileFactory())
				.setThreadPoolSize(10);
		mapKit.setDefaultProvider(DefaultProviders.OpenStreetMaps);
		// mapKit.setCenterPosition(new GeoPosition(0,0));
		mapKit.getMainMap().addMouseListener(new MapMouseListener(this));

		// create a painter who paints the waypoints and connects them
		painter = new PathWaypointPainter<>();

		// set a new renderer to controll how nav points are drawn
		painter.setRenderer(renderer);
		painter.setWaypoints(holder.getNavsSet());
		mapKit.getMainMap().setOverlayPainter(painter);

		JPanel northPanel = new JPanel();
		JCheckBox showNoAirwayNav = new JCheckBox("Show Navs without Airway");
		showNoAirwayNav.setSelected(false);
		showNoAirwayNav.addItemListener(new ShowNoAirwayNavItemListener(this));
		renderer.setShowNoAirwayNav(false);

		northPanel.add(showNoAirwayNav);

		southPanel = new JPanel();
		JLabel nav1Label = new JLabel("NAV 1");
		JLabel nav2Label = new JLabel("NAV 2");
		nav1Text = new JTextField("", 5);
		nav2Text = new JTextField("", 5);
		nav1Text.setEditable(false);
		nav2Text.setEditable(false);

		JButton computePathButton = new JButton("Compute Path");
		computePathButton.setActionCommand("ComputePath");
		computePathButton.addActionListener(this);

		JRadioButton lowerLayer = new JRadioButton("Lower Layer");
		JRadioButton upperLayer = new JRadioButton("Upper Layer");
		JRadioButton bothLayers = new JRadioButton("Both Layers");

		lowerLayer.setSelected(true);
		lowerLayer.setActionCommand("1");
		upperLayer.setActionCommand("2");
		bothLayers.setActionCommand("3");

		layerGroup = new ButtonGroup();

		layerGroup.add(lowerLayer);
		layerGroup.add(upperLayer);
		layerGroup.add(bothLayers);

		JPanel layerPanel = new JPanel();
		layerPanel.add(lowerLayer);
		layerPanel.add(upperLayer);
		layerPanel.add(bothLayers);

		southPanel.add(nav1Label);
		southPanel.add(nav1Text);
		southPanel.add(nav2Label);
		southPanel.add(nav2Text);
		southPanel.add(layerPanel);
		southPanel.add(computePathButton);

		getContentPane().setLayout(new BorderLayout());

		getContentPane().add(mapKit, BorderLayout.CENTER);

		if (!holder.isPrefsOK() && !holder.isDataOK()) {
			showPreferences();
		}
		mapKit.setPreferredSize(new Dimension(800, 600));
		getContentPane().add(southPanel, BorderLayout.SOUTH);
		getContentPane().add(northPanel, BorderLayout.NORTH);
		pack();
		setVisible(true);
	}

	public void selectWaypoint(EarthNav w) {
		if (!toggle) {
			nav1 = w;
			toggle = true;
			nav1Text.setText(w.getIdentifier());
			return;
		}

		nav2 = w;
		toggle = false;
		nav2Text.setText(w.getIdentifier());
		return;
	}

	public void setNoAirwayNavsShown(boolean b) {
		renderer.setShowNoAirwayNav(b);
		repaintMap();
	}

	public void repaintMap() {
		repaint();
	}

	public void setDataHolder(DataHolder h) {
		this.holder = h;
	}

	/**
	 * Start the GUI
	 */
	public void start() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGui();
			}
		});
	}

	public void showPreferences() {
		prefPanel = new JPanel();
		prefPanel.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();

		// add label and textfield to specify the path to the earth_nav.dat file
		JLabel earthNavLabel = new JLabel("Path to earth_nav.dat");
		c.gridx = 0;
		c.gridy = 0;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(earthNavLabel, c);
		earthNavTextField = new JTextField(holder.getPrefs().get(
				"EARTHNAVPATH", "Please specify"), 20);
		c.gridx = 1;
		c.gridy = 0;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(earthNavTextField, c);
		earthNavPathButton = new JButton("Choose file");
		earthNavPathButton.addActionListener(this);
		c.gridx = 2;
		c.gridy = 0;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(earthNavPathButton, c);

		// add label and textfield to specify the path to the earth_fix.dat file
		JLabel fixLabel = new JLabel("Path to earth_fix.dat");
		c.gridx = 0;
		c.gridy = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fixLabel, c);
		fixTextField = new JTextField(holder.getPrefs().get("FIXPATH",
				"Please specify"), 20);
		c.gridx = 1;
		c.gridy = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fixTextField, c);
		fixPathButton = new JButton("Choose file");
		fixPathButton.addActionListener(this);
		c.gridx = 2;
		c.gridy = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fixPathButton, c);

		// add label and textfield to specify the path to the earth_awy.dat file
		JLabel awyLabel = new JLabel("Path to earth_awy.dat");
		c.gridx = 0;
		c.gridy = 2;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(awyLabel, c);
		awyTextField = new JTextField(holder.getPrefs().get("AWYPATH",
				"Please specify"), 20);
		c.gridx = 1;
		c.gridy = 2;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(awyTextField, c);
		awyPathButton = new JButton("Choose file");
		awyPathButton.addActionListener(this);
		c.gridx = 2;
		c.gridy = 2;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(awyPathButton, c);

		// add label and textfield to specify the path to the apt.dat file
		JLabel airportLabel = new JLabel("Path to apt.dat");
		c.gridx = 0;
		c.gridy = 3;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(airportLabel, c);
		airportTextField = new JTextField(holder.getPrefs().get("AIRPORTPATH",
				"Please specify"), 20);
		c.gridx = 1;
		c.gridy = 3;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(airportTextField, c);
		airportPathButton = new JButton("Choose file");
		airportPathButton.addActionListener(this);
		c.gridx = 2;
		c.gridy = 3;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(airportPathButton, c);

		// add label and textfield to specify the path where the FMS plans
		// should be stored
		JLabel fmsStoreLabel = new JLabel("Folder to save FMS plans to ");
		c.gridx = 0;
		c.gridy = 4;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fmsStoreLabel, c);
		fmsStoreTextField = new JTextField(holder.getPrefs().get(
				"FMSSTOREPATH", "Please specify"), 20);
		c.gridx = 1;
		c.gridy = 4;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fmsStoreTextField, c);
		fmsStorePathButton = new JButton("Choose directory");
		fmsStorePathButton.addActionListener(this);
		c.gridx = 2;
		c.gridy = 4;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fmsStorePathButton, c);

		// add save button
		JButton saveButton = new JButton("Save");
		c.gridx = 0;
		c.gridy = 5;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		saveButton.setActionCommand("savePreferences");
		saveButton.addActionListener(this);
		prefPanel.add(saveButton, c);

		// add abort button
		JButton abortButton = new JButton("Abort");
		c.gridx = 1;
		c.gridy = 5;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		abortButton.setActionCommand("abortPreferences");
		abortButton.addActionListener(this);
		prefPanel.add(abortButton, c);

		// remove the map and show the preferences panel
		getContentPane().remove(mapKit);
		getContentPane().add(prefPanel, BorderLayout.CENTER);
		revalidate();
		repaint();
	}

	@Override
	public void actionPerformed(ActionEvent evt) {
		if (evt.getSource() == earthNavPathButton) {
			fileChooser = new JFileChooser();
			if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
				earthNavTextField.setText(fileChooser.getSelectedFile()
						.getAbsolutePath());
			}
		} else if (evt.getSource() == fixPathButton) {
			fileChooser = new JFileChooser();
			if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
				fixTextField.setText(fileChooser.getSelectedFile()
						.getAbsolutePath());
			}
		} else if (evt.getSource() == awyPathButton) {
			fileChooser = new JFileChooser();
			if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
				awyTextField.setText(fileChooser.getSelectedFile()
						.getAbsolutePath());
			}
		} else if (evt.getSource() == airportPathButton) {
			fileChooser = new JFileChooser();
			if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
				airportTextField.setText(fileChooser.getSelectedFile()
						.getAbsolutePath());
			}
		} else if (evt.getSource() == fmsStorePathButton) {
			fileChooser = new JFileChooser();
			fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
				fmsStoreTextField.setText(fileChooser.getSelectedFile()
						.getAbsolutePath());
			}
		} else if (evt.getActionCommand().equals("saveFMS")) {

			// use PrtintWriter to format the output according to the os
			try (PrintWriter out = new PrintWriter(new File(holder.getPrefs()
					.get("FMSSTOREPATH", "default")
					+ System.getProperty("file.separator")
					+ nav1.getIdentifier()
					+ " - "
					+ nav2.getIdentifier()
					+ ".fms"))) {

				out.format("%s%n%s%n%s%n", "I", "3 version", "1");
				out.format("%d%n", painter.getWaypoints().size() - 1);
				for (Waypoint w : painter.getWaypoints()) {
					EarthNav wp = (EarthNav) w;
					out.format(Locale.US, "%d %s 0.000000 %f %f %n", wp
							.getType(), wp.getIdentifier(), wp.getPosition()
							.getLatitude(), wp.getPosition().getLongitude());

				}
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		} else if (evt.getActionCommand().equals("savePreferences")) {
			if (holder.savePreferences(this)) {
				if(!holder.isDataOK()) {
					holder.prepareData();
				}
				getContentPane().remove(prefPanel);
				getContentPane().add(mapKit);
				revalidate();
				repaint();
			} else {
				JLabel errorLabel = new JLabel("Error saving Preferences");
				GridBagConstraints c = new GridBagConstraints();
				c.gridx = 0;
				c.gridy = 6;
				prefPanel.add(errorLabel, c);
				revalidate();
				repaint();
			}
		} else if (evt.getActionCommand().equals("abortPreferences")) {
			getContentPane().remove(prefPanel);
			getContentPane().add(mapKit);
			revalidate();
			repaint();
		} else if (evt.getActionCommand().equals("preferences")) {
			showPreferences();
		} else if (evt.getActionCommand().equals("ComputePath")) {

			if (nav1 == null || nav2 == null) {
				return;
			}
			DecimalFormat df = new DecimalFormat("0.00 NM");

			ArrayList<EarthNav> list = Pathfinder.calculatePath(nav1, nav2,
					Integer.parseInt(layerGroup.getSelection()
							.getActionCommand()), holder.getNavs());
			if (list.size() > 0) {
				painter.setWaypoints(new LinkedHashSet<Waypoint>(list));
				southPanel2 = new JPanel();
				JTextArea text = new JTextArea(10, 30);
				text.setEditable(false);

				StringBuilder b = new StringBuilder();

				for (EarthNav m : list) {
					b.append(m.getType() + " " + m.getIdentifier()
							+ " 00000.000000 " + m.getPosition().getLatitude()
							+ " " + m.getPosition().getLongitude() + "\n");
				}

				JLabel distanceLabel = new JLabel(
						"Distance "
								+ df.format(+list.get(list.size() - 1)
										.getMoveCost() * 0.539956803));
				// JTextField distanceText = new JTextField(df.format(+list.get(
				// list.size() - 1).getMoveCost() * 0.539956803));
				// distanceText.setEditable(false);
				// System.out.println(list.get(list.size()-1).getMoveCost() *
				// 0.539956803);
				text.setText(b.toString());
				text.setEditable(false);
				JScrollPane scroller = new JScrollPane(text);

				JButton back = new JButton("Back");
				back.setActionCommand("BACK");
				back.addActionListener(this);

				JButton saveFMS = new JButton("Save FMS");
				saveFMS.setActionCommand("saveFMS");
				saveFMS.addActionListener(this);

				southPanel2.add(distanceLabel);
				// southPanel2.add(distanceText);
				southPanel2.add(scroller);
				southPanel2.add(back);
				southPanel2.add(saveFMS);

				getContentPane().remove(southPanel);
				getContentPane().add(southPanel2, BorderLayout.SOUTH);
				this.revalidate();
			}
		} else if (evt.getActionCommand().equals("BACK")) {
			getContentPane().remove(southPanel2);
			getContentPane().add(southPanel, BorderLayout.SOUTH);
			painter.setWaypoints(holder.getNavsSet());
			revalidate();
			repaint();
		}

	}
}
